/**
 * 
 */
package be.destin.abdera;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.abdera.ext.opensearch.server.impl.AbstractOpenSearchUrlAdapter;
import org.apache.abdera.i18n.iri.IRI;
import org.apache.abdera.model.Entry;
import org.apache.abdera.model.Feed;
import org.apache.abdera.model.Person;
import org.apache.abdera.protocol.server.RequestContext;
import org.apache.abdera.protocol.server.RequestContext.Scope;
import org.apache.abdera.protocol.server.context.ResponseContextException;
import org.apache.log4j.Logger;

import be.destin.skos.core.Concept;
import be.destin.skos.core.SchemeInterface;
import be.destin.skos.core.SkosManager;
import be.destin.skos.html.SkosHtmlManager;
import be.destin.skos.search.SkosSearch;

/**
 * @author Christophe
 *
 */
public class ConceptSearchUrlAdapter extends AbstractOpenSearchUrlAdapter<Concept> {

	/** log4j category */
	private static Logger log = Logger.getLogger(ConceptSearchUrlAdapter.class);

	SkosManager skosManager;
	ConceptProvider cp;
	
	// TODO: v�rifier si ceci n'est pas compl�tement mauvais dans un environnement multi-thread?
	int totalResult = -1 ;
	
	ConceptSearchUrlAdapter (ConceptProvider cp, SkosManager skosManager) {
		super();
		this.cp = cp;
		this.skosManager = skosManager;
	}

    /**
     * Get the identifier of the feed containing this search results.
     */
	@Override
    protected String getOpenSearchFeedId(RequestContext request) {
		ConceptCollectionAdapter ca = cp.getCollectionAdapter(request);
		if (ca==null) return "";
		return ca.getId(request);
	}

    /**
     * Get the title of the feed containing this search results.
     */
	@Override
    protected String getOpenSearchFeedTitle(RequestContext request) {
		ConceptCollectionAdapter ca = cp.getCollectionAdapter(request);
		if (ca==null) return "";
		String title = ca.getTitle(request);
		// PARAM_SEARCHTERMS+','+PARAM_STARTINDEX+','+PARAM_COUNT+','+PARAM_LANGUAGE+','+PARAM_FILTER+','+PARAM_SKIP+','+PARAM_TOP+','+PARAM_SORT;
		String query = request.getParameter(AbstractCorrectedWorkspaceProvider.PARAM_SEARCHTERMS);
		if (query != null) {
			title += " : "+query;
		}
		return title;
	}

    /**
     * Get the author of the feed containing this search results.
     */
	@Override
    protected Person getOpenSearchFeedAuthor(RequestContext context) {
		Person author = context.getAbdera().getFactory().newAuthor();
		  try {
			ConceptCollectionAdapter ca = cp.getCollectionAdapter(context);
			if (ca == null) return author;
			author.setName(ca.getAuthor(context));
		} catch (ResponseContextException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return author;
	}

    /**
     * Get the update date of the feed containing this search results.
     */
	@Override
    protected Date getOpenSearchFeedUpdatedDate(RequestContext request) {
		ConceptCollectionAdapter ca = cp.getCollectionAdapter(request);
		if (ca==null) return null;
		return ca.getScheme().getLastLoaded();
	}

    /**
     * Do the actual search, returning a list of search results as generic objects.
     */
	@Override
	protected List<Concept> doSearch(RequestContext request, Map<String, String> parameters) {
		
		boolean strictLanguage = false; // RSS Search for a concept....
		
		String language = parameters.get(AbstractCorrectedWorkspaceProvider.PARAM_LANGUAGE);
		if (language == null || language.isEmpty()) {
			language = ConceptCollectionAdapter.getUserLanguage(request);
		} else { // language specified, strict certainly prefered
			strictLanguage = true;
		}
		// PARAM_SEARCHTERMS+','+PARAM_STARTINDEX+','+PARAM_COUNT+','+PARAM_LANGUAGE+','+PARAM_FILTER+','+PARAM_SKIP+','+PARAM_TOP+','+PARAM_SORT;
		String query = parameters.get(AbstractCorrectedWorkspaceProvider.PARAM_SEARCHTERMS);

		if (query==null) query = "";
		SkosHtmlManager skosHtmlManager = new SkosHtmlManager(language, skosManager, SkosHtmlManager.askosiDisplayApplication, cp.getContextPath());
		if (request != null) {
			String aProfile = request.getParameter("profile");
			if (aProfile != null && aProfile.length() > 0) {
				skosHtmlManager.setProfile(aProfile);
			} else {
				aProfile = (String) request.getAttribute(Scope.SESSION, "profile");
				if (aProfile != null && aProfile.length() > 0) {
					skosHtmlManager.setProfile(aProfile);
				}
			}
		}

		String sort = request.getParameter(AbstractCorrectedWorkspaceProvider.PARAM_SORT);
		boolean leftTruncation = false;
		if (!query.isEmpty()) {
			if (query.charAt(0)=='*') {
				leftTruncation = true;
				query = query.substring(1);
			}
		}
		boolean byPieces = false;

		ConceptCollectionAdapter ca = cp.getCollectionAdapter(request);
		SchemeInterface aScheme = null;
		List<Concept> filter = null;
		if (ca != null) {
			aScheme = (SchemeInterface)ca.getScheme();
			String id = ca.getResourceName(request);
			if (id != null && !id.isEmpty()) {
				try {
					Concept entryObj = ca.getEntry(id, request);
					String navigationProperty = request.getTarget().getParameter(AbstractCorrectedWorkspaceProvider.PARAM_NAVIGATION);
					filter = ca.getPropertyEntries(request,entryObj,navigationProperty);
					// TODO: Annuler aScheme si la NavigationProperty est li�e � l'ext�rieur?
				} catch (ResponseContextException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		List<Concept> result = null;
		if (query.isEmpty()) {
			if (filter == null) {
				if (aScheme == null) {
					result = null;
				} else {
					result = new ArrayList<Concept>(aScheme.getConcept());
				}
			} else {
				result = filter;
			}
		} else {
			result = SkosSearch.search(skosHtmlManager,strictLanguage,aScheme, query, !leftTruncation, byPieces, filter);
		}
		if (result == null) {
			result = new ArrayList<Concept>();
		} else {
			if (sort!=null && !sort.isEmpty()) {
				// TODO: Sorting search result: Only "ALFA asc" supported for now!
				/* $orderby Determines what values are used to order a collection of records.
				 *  By default, the order is ascending. Use desc to reverse the order or asc to explicitly set the default.
				 *  If you are retrieving additional sets of data using the next link you should not change the $orderby query option value because this causes unpredictable results.
				 */
			}
			totalResult = result.size();
			if (totalResult > 0) {
				int beg = getOpenSearchFeedStartIndex(request, parameters, result);
				if (beg <= 0) beg = 0;
				else beg--;
				int max = getOpenSearchFeedItemsPerPage(request, parameters, result);
				if (max <= 0) max = totalResult;
				if (beg > 0 || (beg+max < totalResult)) {
					if (beg+max > totalResult) {
						max = totalResult-beg;
					}
					result = result.subList(beg, beg+max);
				}
			}
		}
		return result;
	}

    /**
     * Fill the given empty Atom entry from the given search result object.<br>
     * This method is called once for every search result returned by the {#doSearch(RequestContext, Map<String,
     * String>)} method.
     */
	@Override
    protected void fillEntry(Entry entry, Concept entity) {
		entry.setId(entity.getScheme_About());
	}

    /**
     * Get the total number of results of this search.<br>
     * By default, it's equal to the number of search results: override to provide a different behavior.<br>
     * This element can be explicitly omitted from the feed by returning a negative value.
     */
	@Override
    protected int getOpenSearchFeedTotalResults(RequestContext request,
                                                Map<String, String> parameters,
                                                List<Concept> searchResults) {
		return totalResult;
    }

    /**
     * Get the number of items (entries) contained into this page (feed).<br>
     * By default, it's equal to the number of search results: override to provide a different behavior.<br>
     * This element can be explicitly omitted from the feed by returning a negative value.
     */
	@Override
    protected int getOpenSearchFeedItemsPerPage(RequestContext request,
                                                Map<String, String> parameters,
                                                List<Concept> searchResults) {
		String count = parameters.get(AbstractCorrectedWorkspaceProvider.PARAM_COUNT);
		if (count == null || count.isEmpty()) {
			count = parameters.get(AbstractCorrectedWorkspaceProvider.PARAM_TOP);
		}
		if (count == null || count.isEmpty()) {
			return -1;
		}
		try {
			return Integer.parseInt(count);
		} catch (NumberFormatException e) {
			return -1;
		}
    }

    /**
     * Get the index number of the first result contained into this feed.<br>
     * By default, this element is omitted: override to provide a different behavior.<br>
     * This element can be explicitly omitted from the feed by returning a negative value.
     */
	@Override
    protected int getOpenSearchFeedStartIndex(RequestContext request,
                                              Map<String, String> parameters,
                                              List<Concept> searchResults) {
		boolean skip = false;
		String start = parameters.get(AbstractCorrectedWorkspaceProvider.PARAM_STARTINDEX);
		if (start == null || start.isEmpty()) {
			start = parameters.get(AbstractCorrectedWorkspaceProvider.PARAM_SKIP);
			skip = true;
		}
		if (start == null || start.isEmpty()) {
			return -1;
		}
		try {
			int index = Integer.parseInt(start);
			if (skip) index++;
			return index;
		} catch (NumberFormatException e) {
			return -1;
		}
    }

    /**
     * Post process this feed in order to make custom modifications.<br>
     * By default, this method does nothing: override to provide a different behavior.
     */
	@Override
	protected void postProcess(Feed feed, RequestContext request, Map<String, String> parameters, List<Concept> searchResults) {
		
		String collName = parameters.get("collection");
		if (collName == null) parameters.put("collection", "");
		String entryName = parameters.get("entry");
		if (entryName == null) {
			parameters.put("entry", "");
			parameters.put("target_entry", "");
		} else {
			parameters.put("target_entry", "("+entryName+")");
		}
		
		int[] range = AbstractCorrectedCollectionAdapter.helpPaging(request,feed, totalResult, request.getTarget().getType(), parameters);
		
		for (Entry entry: feed.getEntries()) {
			IRI feedIri = entry.getId();
			if (feedIri == null) continue;
			Concept[] entities = skosManager.getConcept(feedIri.toString());
			if (entities == null) continue;
			for (Concept entity: entities) {
				try {
					ConceptCollectionAdapter ca = cp.getCollectionAdapter(request,entity);
					if (ca==null) continue;
					ca.addEntryDetails(request, entry, feedIri, entity);
					break;
				} catch (ResponseContextException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	
}